using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Microsoft.WowAddonStudio.Lua.RuntimeService.WowBench
{
	/// <summary>
	/// Extend wrapped RichTextBox with some new functionalities.
	/// </summary>
	public sealed partial class RichTextOutputControl
	{
		private readonly Regex linkRegEx = new Regex(@"#([\w]+[;]{1}[\s\S]+)#");
		private const string LinkSeparator = "#";
		private Process webProcess;

		#region Interop Definitions

		/// <summary>
		/// 
		/// </summary>
		internal static class NativeMethods
		{

			[StructLayout(LayoutKind.Sequential)]
			internal struct CHARFORMAT2_STRUCT
			{
				public UInt32 cbSize;
				public UInt32 dwMask;
				public UInt32 dwEffects;
				public Int32 yHeight;
				public Int32 yOffset;
				public Int32 crTextColor;
				public byte bCharSet;
				public byte bPitchAndFamily;
				[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
				public char[] szFaceName;
				public UInt16 wWeight;
				public UInt16 sSpacing;
				public int crBackColor; // Color.ToArgb() -> int
				public int lcid;
				public int dwReserved;
				public Int16 sStyle;
				public Int16 wKerning;
				public byte bUnderlineType;
				public byte bAnimation;
				public byte bRevAuthor;
				public byte bReserved1;
			}

			[DllImport("user32.dll", CharSet = CharSet.Auto)]
			internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

			internal const int WM_USER = 0x0400;
			internal const int EM_GETCHARFORMAT = WM_USER + 58;
			internal const int EM_SETCHARFORMAT = WM_USER + 68;

			internal const int SCF_SELECTION = 0x0001;
			internal const int SCF_WORD = 0x0002;
			internal const int SCF_ALL = 0x0004;

			#region CHARFORMAT2 Flags

			internal const UInt32 CFE_BOLD = 0x0001;
			internal const UInt32 CFE_ITALIC = 0x0002;
			internal const UInt32 CFE_UNDERLINE = 0x0004;
			internal const UInt32 CFE_STRIKEOUT = 0x0008;
			internal const UInt32 CFE_PROTECTED = 0x0010;
			internal const UInt32 CFE_LINK = 0x0020;
			internal const UInt32 CFE_AUTOCOLOR = 0x40000000;
			internal const UInt32 CFE_SUBSCRIPT = 0x00010000;		/* Superscript and subscript are */
			internal const UInt32 CFE_SUPERSCRIPT = 0x00020000;		/*  mutually exclusive			 */

			internal const int CFM_SMALLCAPS = 0x0040;			/* (*)	*/
			internal const int CFM_ALLCAPS = 0x0080;			/* Displayed by 3.0	*/
			internal const int CFM_HIDDEN = 0x0100;			/* Hidden by 3.0 */
			internal const int CFM_OUTLINE = 0x0200;			/* (*)	*/
			internal const int CFM_SHADOW = 0x0400;			/* (*)	*/
			internal const int CFM_EMBOSS = 0x0800;			/* (*)	*/
			internal const int CFM_IMPRINT = 0x1000;			/* (*)	*/
			internal const int CFM_DISABLED = 0x2000;
			internal const int CFM_REVISED = 0x4000;

			internal const int CFM_BACKCOLOR = 0x04000000;
			internal const int CFM_LCID = 0x02000000;
			internal const int CFM_UNDERLINETYPE = 0x00800000;		/* Many displayed by 3.0 */
			internal const int CFM_WEIGHT = 0x00400000;
			internal const int CFM_SPACING = 0x00200000;		/* Displayed by 3.0	*/
			internal const int CFM_KERNING = 0x00100000;		/* (*)	*/
			internal const int CFM_STYLE = 0x00080000;		/* (*)	*/
			internal const int CFM_ANIMATION = 0x00040000;		/* (*)	*/
			internal const int CFM_REVAUTHOR = 0x00008000;


			internal const UInt32 CFM_BOLD = 0x00000001;
			internal const UInt32 CFM_ITALIC = 0x00000002;
			internal const UInt32 CFM_UNDERLINE = 0x00000004;
			internal const UInt32 CFM_STRIKEOUT = 0x00000008;
			internal const UInt32 CFM_PROTECTED = 0x00000010;
			internal const UInt32 CFM_LINK = 0x00000020;
			internal const UInt32 CFM_SIZE = 0x80000000;
			internal const UInt32 CFM_COLOR = 0x40000000;
			internal const UInt32 CFM_FACE = 0x20000000;
			internal const UInt32 CFM_OFFSET = 0x10000000;
			internal const UInt32 CFM_CHARSET = 0x08000000;
			internal const UInt32 CFM_SUBSCRIPT = CFE_SUBSCRIPT | CFE_SUPERSCRIPT;
			internal const UInt32 CFM_SUPERSCRIPT = CFM_SUBSCRIPT;

			internal const byte CFU_UNDERLINENONE = 0x00000000;
			internal const byte CFU_UNDERLINE = 0x00000001;
			internal const byte CFU_UNDERLINEWORD = 0x00000002; /* (*) displayed as ordinary underline	*/
			internal const byte CFU_UNDERLINEDOUBLE = 0x00000003; /* (*) displayed as ordinary underline	*/
			internal const byte CFU_UNDERLINEDOTTED = 0x00000004;
			internal const byte CFU_UNDERLINEDASH = 0x00000005;
			internal const byte CFU_UNDERLINEDASHDOT = 0x00000006;
			internal const byte CFU_UNDERLINEDASHDOTDOT = 0x00000007;
			internal const byte CFU_UNDERLINEWAVE = 0x00000008;
			internal const byte CFU_UNDERLINETHICK = 0x00000009;
			internal const byte CFU_UNDERLINEHAIRLINE = 0x0000000A; /* (*) displayed as ordinary underline	*/

			#endregion

		}

	
		#endregion

		#region Private Functions

		/// <summary>
		/// Insert a given text at a given position as a link. The link text is followed by
		/// a hash (#) and the given hyperlink text, both of them invisible.
		/// </summary>
		/// <param name="text">The text to be inserted.</param>
		/// <param name="hyperlink">The hyperlink string to be inserted.</param>
		/// <param name="position">The text position.</param>
		private void InsertLink(string text, string hyperlink, int position)
		{
			if (position < 0 || position > Text.Length)
				throw new ArgumentOutOfRangeException("position");

			textBox.SelectionStart = position;
			textBox.SelectedRtf = @"{\rtf1\ansi " + text + @"\v #" + hyperlink + @"\v0}";
			textBox.Select(position, text.Length + hyperlink.Length + 1);
			SetSelectionLink(true);
			textBox.Select(position + text.Length + hyperlink.Length + 1, 0);
		}

		/// <summary>
		/// Set the current selection to link.
		/// </summary>
		/// <param name="link">True if selection mark as link,
		/// False clear link in selection</param>
		private void SetSelectionLink(bool link)
		{
			SetSelectionStyle(NativeMethods.CFM_LINK, link ? NativeMethods.CFE_LINK : 0);
		}


		/// <summary>
		/// 
		/// </summary>
		/// <param name="mask"></param>
		/// <param name="effect"></param>
		private void SetSelectionStyle(UInt32 mask, UInt32 effect)
		{
			NativeMethods.CHARFORMAT2_STRUCT cf = new NativeMethods.CHARFORMAT2_STRUCT();
			cf.cbSize = (UInt32)Marshal.SizeOf(cf);
			cf.dwMask = mask;
			cf.dwEffects = effect;

			IntPtr wpar = new IntPtr(NativeMethods.SCF_SELECTION);
			IntPtr lpar = Marshal.AllocCoTaskMem(Marshal.SizeOf(cf));
			Marshal.StructureToPtr(cf, lpar, false);

			NativeMethods.SendMessage(textBox.Handle, NativeMethods.EM_SETCHARFORMAT, wpar, lpar);

			Marshal.FreeCoTaskMem(lpar);
		}

		#endregion

		/// <summary>
		/// Gets or sets a value indicating whether or not the <see cref="System.Windows.Forms.RichTextBox"/> 
		/// will automatically format a Uniform Resource Locator (URL) when it is typed into the control.
		/// </summary>
		public bool DetectUrls
		{
			get { return textBox.DetectUrls; }
			set { textBox.DetectUrls = value; }
		}

		/// <summary>
		/// Insert a given text at at the current input position as a link.
		/// </summary>
		/// <param name="text">The text to be inserted.</param>
		/// <param name="hyperlink">The hyperlink string to be inserted.</param>
		public void AppendLink(string text, string hyperlink)
		{
			InsertLink(text, hyperlink, textBox.SelectionStart);
		}

		/// <summary>
		/// Insert a given text at at the current input position as a link.
		/// </summary>
		/// <param name="text">The text to be inserted.</param>
		public void AppendLink(string text)
		{
			if (text != null)
			{
				try
				{
					string hyperlinkText = linkRegEx.Match(text).Groups[0].Captures[0].Value;
					string hyperlink =
						hyperlinkText.Substring(hyperlinkText.LastIndexOf(";") + 1,
						                        hyperlinkText.LastIndexOf(LinkSeparator) - hyperlinkText.LastIndexOf(";") - 1).Trim();
					string linkName =
						hyperlinkText.Substring(hyperlinkText.IndexOf(LinkSeparator) + 1, hyperlinkText.LastIndexOf(";") - 1).Trim();

					if (textBox.DetectUrls)
					{
						textBox.AppendText(String.Format("{0} ({1}){2}", text.Substring(0, text.IndexOf(LinkSeparator) - 1),
						                                 hyperlink,
						                                 text.Substring(text.LastIndexOf(LinkSeparator) + 1,
						                                                text.Length - text.LastIndexOf(LinkSeparator) - 1)));
					}
					else
					{
						textBox.AppendText(String.Concat(text.Substring(0, text.IndexOf(LinkSeparator) - 1), " "));
						InsertLink(linkName, hyperlink, textBox.SelectionStart);
						textBox.AppendText(text.Substring(text.LastIndexOf(LinkSeparator) + 1,
						                                  text.Length - text.LastIndexOf(LinkSeparator) - 1));
					}
				}
				catch (Exception e)
				{
					Trace.WriteLine(e);
				}
			}
		}

		#region Event Handlers


		/// <summary>
		/// Called when [link clicked].
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.Windows.Forms.LinkClickedEventArgs"/> instance containing the event data.</param>
		private void OnLinkClicked(object sender, LinkClickedEventArgs e)
		{
			try
			{
				webProcess = Process.Start(CleanLinkText(e.LinkText));
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex);
			}
		}


		/// <summary>
		/// Cleans the link text.
		/// </summary>
		/// <param name="linkText">The link text.</param>
		/// <returns></returns>
		private static string CleanLinkText(string linkText)
		{
			string result = linkText;
			if (linkText.Contains(LinkSeparator))
			{
				result = linkText.Substring(linkText.LastIndexOf(LinkSeparator) + 1,
				                            linkText.Length - linkText.LastIndexOf(LinkSeparator) - 1);
			}
			return result;
		}

		#endregion

	}
}